using System;

using OR.Reuse.Persistence;
using OR.Lazarus.Business;
using OR.Lazarus.Config;

using Notation = OR.Lazarus.Business.Entities.Notation;
using SourceModel = OR.Lazarus.Library.DataTier.Source;
using CompiledModel = OR.Lazarus.Library.DataTier.Compiled;

namespace OR.Lazarus.Library.DataTier
{
    /// <summary>
    /// Summary description for SourceCompiler.
    /// </summary>
    public class SourceCompiler : CompilerBase<SourceModel.ProjectDetail, CompiledModel.ProjectDetail, SourceValidator>
    {
        private Notation.NotationDetail mNotationMap;

        public SourceCompiler()
        {
            mNotationMap = new Notation.NotationDetail();
        }

        protected override void OnBeforeCompile(EventArgs e)
        {
            base.OnBeforeCompile(e);

            this.OnInitializeGeneralization();

            this.OnInitializeFields();
        }

        #region Properties
        public Notation.NotationDetail NotationMap
        {
            get
            {
                return (mNotationMap);
            }
            set
            {
                mNotationMap = value;
            }
        }
        #endregion

        #region Initialization
        protected virtual void OnInitializeFields()
        {
            foreach (SourceModel.ObjectDetail vObject in mSourceProject.Objects)
            {
                vObject.Fields.Initialize(mNotationMap);

                vObject.Initialize();
            }

            foreach (SourceModel.IntersectionDetail vIntersection in mSourceProject.Intersections)
            {
                vIntersection.Fields.Initialize(mNotationMap);

                vIntersection.Initialize();
            }
        }

        protected virtual void OnInitializeGeneralization()
        {
            foreach (SourceModel.ObjectDetail vObject in mSourceProject.Objects)
            {
                if (String.IsNullOrEmpty(vObject.DBTable) && (vObject.Implements.Count == 0))
                {
                    vObject.DBTable = vObject.Name;
                }
            }

            foreach (SourceModel.ObjectDetail vObject in mSourceProject.Objects)
            {
                this.OnInitializeGeneralization(vObject);
            }
        }

        protected virtual void OnInitializeGeneralizationCategory(SourceModel.ObjectDetail pSource)
        {
            SourceModel.ObjectCollection vObjectsImplemented = null;
            SourceModel.FieldDetail vSourcePKField = null;
            SourceModel.FieldDetail vTargetPKField = pSource.GetPKField();
            SourceModel.IndexCollection vTargetPKIndexes = null;
            SourceModel.IndexDetail vTargetPKIndex = null;
            SourceModel.IndexDetail vTargetPKFKIndex = null;

            vObjectsImplemented = pSource.Ancestors;

            // Set the DBTable to the current object.
            pSource.DBTable = pSource.Name;

            // Check for a PK.
            if (vTargetPKField == null)
            {
                foreach (SourceModel.ObjectDetail vObject in vObjectsImplemented)
                {
                    vSourcePKField = vObject.GetPKField();

                    if (vSourcePKField != null)
                    {
                        vTargetPKField = vSourcePKField.CloneTyped();

                        if (vTargetPKField.DataType == CompilerSettings.DATATYPE_IDENTITY)
                        {
                            vTargetPKField.DataType = CompilerSettings.DATATYPE_INT;
                            vTargetPKField.AutoGenerate = false;
                        }

                        vTargetPKIndexes = vTargetPKField.Indexes.GetByType(CompilerSettings.INDEXTYPE_PRIMARY);

                        vTargetPKIndex = vTargetPKIndexes[0];
                        vTargetPKIndex.Name = CompilerSettings.NOTATION_PRIMARYKEY + pSource.Name;

                        break;
                    }
                }

                if (vTargetPKField == null)
                {
                    throw new ApplicationException(String.Format("Object [{0}] does not have a primary key defined in any Generalization object."));
                }

                pSource.Fields.Add(vTargetPKField);
            }

            foreach (SourceModel.ImplementDetail vImplement in pSource.Implements)
            {
                vTargetPKFKIndex = new SourceModel.IndexDetail();
                vTargetPKFKIndex.Accessor = vTargetPKField.Alias;
                vTargetPKFKIndex.Alias = vTargetPKField.Alias;
                vTargetPKFKIndex.Type = CompilerSettings.INDEXTYPE_FOREIGN;
                vTargetPKFKIndex.Object = mSourceProject.Objects[vImplement.Ref].DBTable;
                vTargetPKFKIndex.CascadeDelete = true;

                vTargetPKField.Indexes.Add(vTargetPKFKIndex);
            }
        }

        protected virtual void OnInitializeGeneralizationAggregate(SourceModel.ObjectDetail pSource)
        {
            // Set the DBTable to the current object.
            pSource.DBTable = pSource.Parent.DBTable;
        }

        protected virtual void OnInitializeGeneralization(SourceModel.ObjectDetail pSource)
        {
            if (pSource.Implements.Count > 0)
            {
                switch (pSource.Implements[0].DBMethod)
                {
                    case "Category":
                        {
                            this.OnInitializeGeneralizationCategory(pSource);

                            break;
                        }
                    case "Aggregate":
                        {
                            this.OnInitializeGeneralizationAggregate(pSource);

                            break;
                        }
                }
            }
        }

        #endregion

        #region Standards/Defaults Application
        protected override void OnApplyDefaults()
        {
            foreach (SourceModel.ObjectDetail vObject in mSourceProject.Objects)
            {
                if (vObject.Implements.Count == 0)
                {
                    this.OnApplyDefaults(vObject);
                }
            }

            foreach (SourceModel.IntersectionDetail vIntersection in mSourceProject.Intersections)
            {
                this.OnApplyDefaults(vIntersection);
            }
        }

        protected virtual void OnApplyDefaults(SourceModel.ObjectDetail pSource)
        {
            foreach (SourceModel.FieldDetail vField in mSourceProject.DefaultSetting.Fields)
            {
                if (pSource.Fields[vField.Name] == null)
                {
                    pSource.Fields.Add(vField.CloneTyped());
                }
            }
        }

        protected virtual void OnApplyDefaults(SourceModel.IntersectionDetail pSource)
        {
            foreach (SourceModel.FieldDetail vField in mSourceProject.DefaultSetting.Fields)
            {
                if (pSource.Fields[vField.Name] == null)
                {
                    pSource.Fields.Add(vField.CloneTyped());
                }
            }
        }
        #endregion

        #region Type Management
        protected virtual string GetDBType(string pBaseType)
        {
            string vReturn = String.Empty;

            switch (pBaseType)
            {
                case CompilerSettings.DATATYPE_IDENTITY:
                    {
                        vReturn = CompilerSettings.DATATYPE_INT;

                        break;
                    }
                default:
                    {
                        vReturn = pBaseType;

                        break;
                    }
            }

            return (vReturn);
        }
        #endregion

        #region Compiler
        #region Project Compilation
        protected override void OnCompileProject(SourceModel.ProjectDetail pSource)
        {
            mCompiledProject.Description = pSource.Description;
            mCompiledProject.DefaultNamespace = pSource.DefaultNamespace;
            mCompiledProject.Name = pSource.Name;
            mCompiledProject.Owner = pSource.Owner;
            mCompiledProject.User = pSource.User;
            mCompiledProject.NamespacePrefix = pSource.NamespacePrefix;
            mCompiledProject.NamespaceURI = pSource.NamespaceURI;
            mCompiledProject.BusinessConcept = pSource.BusinessConcept;
            mCompiledProject.UseParameterPrefix = pSource.UseParameterPrefix;

            this.OnCompileDataSource(pSource.DataSource);
            this.OnCompileSecurity(pSource.Security);
            this.OnCompileObjects(pSource.Objects);
            this.OnCompileIntersections(pSource.Intersections);
            this.OnCompileGeneralizations(mCompiledProject.Objects);
            this.OnCompilePackages(pSource.Packages);
            this.OnCompileMethods();
            this.OnCompileBusinessViews(pSource.BusinessViews);
        }

        protected virtual void OnCompileDataSource(SourceModel.DataSourceDetail pSource)
        {
            mCompiledProject.DataSource.Name = pSource.Name;
            mCompiledProject.DataSource.Location = pSource.Location;
            mCompiledProject.DataSource.Schema = pSource.Schema;
            mCompiledProject.DataSource.User = pSource.User;
            mCompiledProject.DataSource.Password = pSource.Password;
        }
        #endregion

        #region Generalization Compilation
        protected virtual void OnCompileGeneralizations(CompiledModel.ObjectCollection pSource)
        {
            foreach (CompiledModel.ObjectDetail vObject in pSource)
            {
                this.OnCompileGeneralizations(vObject);
            }
        }

        protected virtual void OnCompileGeneralizations(CompiledModel.ObjectDetail pSource)
        {
            CompiledModel.ObjectCollection vInheritanceChain = pSource.GetInheritanceChain(mCompiledProject.Objects);
            CompiledModel.ObjectRefDetail vCompiledObjectRef = null;

            // Add the PK from the root table, is it will govern all following PK's.
            foreach (CompiledModel.ObjectDetail vObject in vInheritanceChain)
            {
                vCompiledObjectRef = new CompiledModel.ObjectRefDetail();

                vCompiledObjectRef.Ref = vObject.Name;

                foreach (CompiledModel.FieldDetail vField in vObject.Fields)
                {
                    vCompiledObjectRef.Fields.Add(vField);
                }

                pSource.AllFields.Add(vCompiledObjectRef);
            }
        }
        #endregion

        #region Business View Compilation
        protected virtual void OnCompileBusinessViews(SourceModel.BusinessViewCollection pSource)
        {
            foreach (SourceModel.BusinessViewDetail vBusinessView in pSource)
            {
                this.OnCompileBusinessView(vBusinessView);
            }
        }

        protected virtual void OnCompileBusinessView(SourceModel.BusinessViewDetail pSource)
        {
            CompiledModel.BusinessViewDetail vCompiledView = new CompiledModel.BusinessViewDetail();

            CompiledModel.DataTableDetail vCompiledTable = null;
            CompiledModel.DataColumnDetail vCompiledColumn = null;
            CompiledModel.FieldDetail vRefField = null;

            //			CompiledModel.RelationDetail				vCompiledRelation				= null;
            //			CompiledModel.RelationReferenceDetail	vCompiledRelationReference	= null;

            vCompiledView.Name = pSource.Name;
            vCompiledView.Description = pSource.Description;
            vCompiledView.Context = pSource.Context;

            foreach (SourceModel.DataTableDetail vDataTable in pSource.DataTables)
            {
                vCompiledTable = new CompiledModel.DataTableDetail();

                vCompiledTable.Ref = vDataTable.Ref;
                vCompiledTable.Name = vDataTable.Name;
                vCompiledTable.Description = vDataTable.Description;

                foreach (SourceModel.DataColumnDetail vDataColumn in vDataTable.Columns)
                {
                    vCompiledColumn = new CompiledModel.DataColumnDetail();

                    vRefField = mCompiledProject.FindFieldByObject(vDataTable.Ref, vDataColumn.Ref);

                    vCompiledColumn.Alias = vRefField.Alias;
                    vCompiledColumn.AutoGenerate = vRefField.AutoGenerate;
                    vCompiledColumn.DataType = vRefField.DataType;
                    vCompiledColumn.DBName = vRefField.DBName;
                    vCompiledColumn.DBType = vRefField.DBType;
                    vCompiledColumn.DefaultValue = vRefField.DefaultValue;
                    vCompiledColumn.Description = vRefField.Description;
                    vCompiledColumn.FriendlyName = vRefField.FriendlyName;
                    vCompiledColumn.LogicalName = vRefField.LogicalName;
                    vCompiledColumn.Name = vRefField.Name;
                    vCompiledColumn.ReadOnly = vRefField.ReadOnly;
                    vCompiledColumn.Required = vRefField.Required;
                    vCompiledColumn.Scale = vRefField.Scale;
                    vCompiledColumn.Size = vRefField.Size;
                    vCompiledColumn.TableOnly = vRefField.TableOnly;

                    vCompiledTable.Columns.Add(vCompiledColumn);
                }

                vCompiledView.DataTables.Add(vCompiledTable);
            }

            //			foreach ( SourceModel.RelationDetail vRelation in pSource.Relationships )
            //			{
            //				
            //			}

            mCompiledProject.BusinessViews.Add(vCompiledView);
        }

        #endregion

        #region Intersection Compilation
        protected virtual void OnCompileIntersections(SourceModel.IntersectionCollection pSource)
        {
            foreach (SourceModel.IntersectionDetail vIntersection in pSource)
            {
                this.OnCompileIntersection(vIntersection);
            }
        }

        protected virtual void OnCompileIntersection(SourceModel.IntersectionDetail pSource)
        {
            CompiledModel.ObjectDetail vCompiledObject = new CompiledModel.ObjectDetail();
            CompiledModel.FieldDetail vCompiledField = null;
            CompiledModel.IndexDetail vCompiledIndex = null;

            SourceModel.FieldDetail vSourcePKField = null;
            CompiledModel.FieldDetail vTargetPKField = null;

            vCompiledObject.Name = pSource.Name;
            vCompiledObject.Type = CompilerSettings.OBJECTTYPE_INTERSECTION;
            vCompiledObject.Description = pSource.Description;
            vCompiledObject.Alias = pSource.Alias;
            vCompiledObject.Filename = pSource.Name;
            vCompiledObject.Namespace = mCompiledProject.DefaultNamespace;
            vCompiledObject.FriendlyName = pSource.FriendlyName;
            vCompiledObject.LogicalName = pSource.LogicalName;
            vCompiledObject.Filegroup = pSource.Filegroup;

            foreach (SourceModel.ReferenceDetail vReference in pSource.References)
            {
                vCompiledField = new CompiledModel.FieldDetail();
                vTargetPKField = mCompiledProject.Objects[vReference.Object].GetPKField();

                vCompiledField.Name = vReference.Name;
                vCompiledField.Alias = vReference.Alias;
                vCompiledField.DataType = this.GetDBType(vTargetPKField.DataType);
                vCompiledField.DefaultValue = vTargetPKField.DefaultValue;
                vCompiledField.Description = vReference.Description;
                vCompiledField.Required = true;
                vCompiledField.Scale = vTargetPKField.Scale;
                vCompiledField.Size = vTargetPKField.Size;
                vCompiledField.DBName = vReference.Name;

                if (!vCompiledField.IsPrimaryKey)
                {
                    vCompiledField.DBName = vReference.Name + CompilerSettings.DBNAME_FKSUFFIX;
                }

                vCompiledIndex = new CompiledModel.IndexDetail();
                vCompiledIndex.Type = CompilerSettings.INDEXTYPE_FOREIGN;
                vCompiledIndex.Object = vReference.Object;
                vCompiledIndex.Alias = vReference.Alias;
                vCompiledIndex.Accessor = vReference.Accessor;
                vCompiledIndex.ConstraintName = CompilerSettings.NOTATION_FOREIGNKEY + pSource.Name + "_" + vReference.Name;
                vCompiledIndex.Name = CompilerSettings.NOTATION_INDEX + pSource.Name + "_" + vReference.Name;

                vCompiledField.Indexes.Add(vCompiledIndex);
                vCompiledObject.Fields.Add(vCompiledField);
            }

            foreach (SourceModel.FieldDetail vField in pSource.Fields)
            {
                vCompiledField = new CompiledModel.FieldDetail();

                vCompiledField.Name = vField.Name;
                vCompiledField.Alias = vField.Alias;
                vCompiledField.AutoGenerate = (bool)vField.AutoGenerate;
                vCompiledField.DataType = vField.DataType;
                vCompiledField.DefaultValue = vField.DefaultValue;
                vCompiledField.Description = vField.Description;
                vCompiledField.FriendlyName = vField.FriendlyName;
                vCompiledField.LogicalName = vField.LogicalName;
                vCompiledField.ReadOnly = (bool)vField.ReadOnly;
                vCompiledField.Required = (bool)vField.Required;
                vCompiledField.Scale = vField.Scale;
                vCompiledField.Size = vField.Size;
                vCompiledField.TableOnly = (bool)vField.TableOnly;
                vCompiledField.DBName = vField.Name;

                if ((vField.IsForeignKey) && !(vField.IsPrimaryKey))
                {
                    vCompiledField.DBName += CompilerSettings.DBNAME_FKSUFFIX;

                    vSourcePKField = pSource.GetPKField();

                    vCompiledField.DataType = this.GetDBType(vSourcePKField.DataType);
                    vCompiledField.DBType = vCompiledField.DataType;
                }
                else
                {
                    vCompiledField.DBType = this.GetDBType(vField.DataType);
                }


                foreach (SourceModel.IndexDetail vIndex in vField.Indexes)
                {
                    vCompiledIndex = new CompiledModel.IndexDetail();

                    vCompiledIndex.Alias = vIndex.Alias;
                    vCompiledIndex.Accessor = vIndex.Accessor;
                    vCompiledIndex.Description = vIndex.Description;
                    vCompiledIndex.Name = vIndex.Name;
                    vCompiledIndex.ConstraintName = vIndex.ConstraintName;
                    vCompiledIndex.Object = vIndex.Object;
                    vCompiledIndex.OwnerField = vIndex.OwnerField;
                    vCompiledIndex.Type = vIndex.Type;
                    vCompiledIndex.Filegroup = vIndex.Filegroup;
                    vCompiledIndex.CascadeDelete = vIndex.CascadeDelete;

                    vCompiledField.Indexes.Add(vCompiledIndex);
                }

                vCompiledObject.Fields.Add(vCompiledField);
            }

            mCompiledProject.Objects.Add(vCompiledObject);

            // If no clustered index is set, make the PK clustered.
            CompiledModel.FieldDetail vPKField = vCompiledObject.GetPKField();
            CompiledModel.FieldDetail vClusteredField = vCompiledObject.GetClusteredField();

            if (vClusteredField == null)
            {
                CompiledModel.IndexDetail vNewClusteredIndex = new CompiledModel.IndexDetail();

                vNewClusteredIndex.Type = CompilerSettings.INDEXTYPE_CLUSTERED;

                vPKField.Indexes.Add(vNewClusteredIndex);
            }
        }
        #endregion

        #region Object Compilation
        protected virtual void OnCompileObjects(SourceModel.ObjectCollection pSource)
        {
            foreach (SourceModel.ObjectDetail vObject in pSource)
            {
                this.OnCompileObject(vObject);
            }
        }

        protected virtual void OnCompileObject(SourceModel.ObjectDetail pSource)
        {
            CompiledModel.ObjectDetail vCompiledObject = new CompiledModel.ObjectDetail();

            this.OnCompileObject(pSource, vCompiledObject);
            mCompiledProject.Objects.Add(vCompiledObject);

            CompiledModel.TableDetail vCompiledTable = new CompiledModel.TableDetail();

            this.OnCompileObject(pSource, vCompiledTable);

            mCompiledProject.Tables.Add(vCompiledTable);
        }

        protected virtual void OnCompileObject(SourceModel.ObjectDetail pSource, CompiledModel.ObjectDetail pTarget)
        {
            CompiledModel.FieldDetail vCompiledField = null;
            CompiledModel.IndexDetail vCompiledIndex = null;
            CompiledModel.ImplementDetail vCompiledImplement = null;
            CompiledModel.PermissionDetail vCompiledPermission = null;
            CompiledModel.CollectionDetail vCompiledCollection = null;
            CompiledModel.CapabilityDetail vCompiledCapability = null;
            CompiledModel.DataRowDetail vCompiledDataRow = null;
            CompiledModel.DataValueDetail vCompiledDataValue = null;
            CompiledModel.AttributeFlagDetail vCompiledFlag = null;

            SourceModel.ObjectDetail vSourceFKRefObject = null;
            SourceModel.FieldDetail vSourcePKField = null;

            pTarget.Name = pSource.Name;
            pTarget.PluralName = pSource.PluralName;
            pTarget.LogicalName = pSource.LogicalName;
            pTarget.FriendlyName = pSource.FriendlyName;
            pTarget.Description = pSource.Description;
            pTarget.Alias = pSource.Alias;
            pTarget.Filename = pSource.Name;
            pTarget.Namespace = mCompiledProject.DefaultNamespace;
            pTarget.Filegroup = pSource.Filegroup;
            pTarget.DBTable = pSource.DBTable;
            pTarget.TitleField = pSource.TitleField;
            pTarget.Project = mCompiledProject;
            pTarget.Type = pSource.Type;

            foreach (SourceModel.AttributeFlagDetail vFlag in pSource.Flags)
            {
                vCompiledFlag = new CompiledModel.AttributeFlagDetail();

                vCompiledFlag.Name = vFlag.Name;
                vCompiledFlag.Value = vFlag.Value;

                pTarget.Flags.Add(vCompiledFlag);
            }

            foreach (SourceModel.ImplementDetail vImplement in pSource.Implements)
            {
                vCompiledImplement = new CompiledModel.ImplementDetail();

                vCompiledImplement.Ref = vImplement.Ref;

                pTarget.Implements.Add(vCompiledImplement);
            }

            foreach (SourceModel.PermissionDetail vPermission in pSource.Permissions)
            {
                vCompiledPermission = new CompiledModel.PermissionDetail();

                vCompiledPermission.Role = vPermission.Role;

                foreach (SourceModel.CapabilityDetail vCapability in vPermission.Capabilities)
                {
                    vCompiledCapability = new CompiledModel.CapabilityDetail();

                    vCompiledCapability.Name = vCapability.Name;

                    vCompiledPermission.Capabilities.Add(vCompiledCapability);
                }

                pTarget.Permissions.Add(vCompiledPermission);
            }

            foreach (SourceModel.DataRowDetail vDataRow in pSource.InitialData)
            {
                vCompiledDataRow = new CompiledModel.DataRowDetail();

                foreach (SourceModel.DataValueDetail vDataValue in vDataRow.DataValues)
                {
                    vCompiledDataValue = new CompiledModel.DataValueDetail();

                    vCompiledDataValue.Description = vDataValue.Description;
                    vCompiledDataValue.Field = vDataValue.Field;
                    vCompiledDataValue.Name = vDataValue.Name;
                    vCompiledDataValue.Value = vDataValue.Value;

                    vCompiledDataRow.DataValues.Add(vCompiledDataValue);
                }

                pTarget.InitialData.Add(vCompiledDataRow);
            }

            foreach (SourceModel.FieldDetail vField in pSource.Fields)
            {
                vCompiledField = new CompiledModel.FieldDetail();

                vCompiledField.Name = vField.Name;
                vCompiledField.Alias = vField.Alias;
                vCompiledField.AutoGenerate = (bool)vField.AutoGenerate;
                vCompiledField.DataType = vField.DataType;
                vCompiledField.DefaultValue = vField.DefaultValue;
                vCompiledField.Description = vField.Description;
                vCompiledField.FriendlyName = vField.FriendlyName;
                vCompiledField.LogicalName = vField.LogicalName;
                vCompiledField.ReadOnly = (bool)vField.ReadOnly;
                vCompiledField.Required = (bool)vField.Required;
                vCompiledField.Scale = vField.Scale;
                vCompiledField.Size = vField.Size;
                vCompiledField.TableOnly = (bool)vField.TableOnly;
                vCompiledField.DBName = vField.Name;

                if (vField.IsForeignKey && !vField.IsPrimaryKey)
                {
                    vCompiledField.DBName += CompilerSettings.DBNAME_FKSUFFIX;
                    vSourceFKRefObject = mSourceProject.Objects[vField.GetForeignKey().Object];

                    vSourcePKField = vSourceFKRefObject.GetPKField();

                    vCompiledField.DataType = this.GetDBType(vSourcePKField.DataType);
                    vCompiledField.DBType = vCompiledField.DataType;
                }
                else
                {
                    vCompiledField.DBType = this.GetDBType(vField.DataType);
                }

                foreach (SourceModel.IndexDetail vIndex in vField.Indexes)
                {
                    vCompiledIndex = new CompiledModel.IndexDetail();

                    vCompiledIndex.Alias = vIndex.Alias;
                    vCompiledIndex.Accessor = vIndex.Accessor;
                    vCompiledIndex.Description = vIndex.Description;
                    vCompiledIndex.Name = vIndex.Name;
                    vCompiledIndex.ConstraintName = vIndex.ConstraintName;
                    vCompiledIndex.Object = vIndex.Object;
                    vCompiledIndex.OwnerField = vIndex.OwnerField;
                    vCompiledIndex.Type = vIndex.Type;
                    vCompiledIndex.Filegroup = vIndex.Filegroup;
                    vCompiledIndex.CascadeDelete = vIndex.CascadeDelete;

                    vCompiledField.Indexes.Add(vCompiledIndex);
                }

                pTarget.Fields.Add(vCompiledField);
            }

            // Compile explicitly-defined collections.
            SourceModel.ObjectDetail vBindingObject = null;
            SourceModel.FieldDetail vBindingField = null;

            foreach (SourceModel.CollectionDetail vCollection in pSource.Collections)
            {
                vCompiledCollection = new CompiledModel.CollectionDetail();

                vCompiledCollection.Name = vCollection.Name;

                if (String.IsNullOrEmpty(vCollection.BindingObject))
                {
                    vCompiledCollection.BindingObject = vCollection.Object;
                }
                else
                {
                    vCompiledCollection.BindingObject = vCollection.BindingObject;
                }

                // Make sure the Object Exists.
                vBindingObject = mSourceProject.Objects[vCompiledCollection.BindingObject];

                if (vBindingObject == null)
                {
                    throw new ApplicationException("Compilation of Object [ " + pSource.Name + " ] failed: The Collection [ " + vCollection.Name + " ] specifies Binding Object [ " + vCompiledCollection.BindingObject + " ], which does not exist.");
                }
                else
                {
                    vBindingField = vBindingObject.Fields[vCollection.BindingField];

                    if (vBindingField == null)
                    {
                        //						throw new ApplicationException( "Compilation of Object [ " + pSource.Name + " ] failed: The Collection [ " + vCollection.Name + " ] specifies Binding Field [ " + vCompiledCollection.BindingObject + "." + vCollection.BindingField + " ]. The Field does not exist." );
                    }
                    else
                    {
                        if (!((vBindingField.IsForeignKey) && (vBindingField.Indexes.GetByType(CompilerSettings.INDEXTYPE_FOREIGN)[0].Object == pSource.Name)))
                        {
                            throw new ApplicationException("Compilation of Object [ " + pSource.Name + " ] failed: The Collection [ " + vCollection.Name + " ] specifies Binding Field [ " + vCompiledCollection.BindingObject + "." + vCollection.BindingField + " ]. The Field must be a foreign key to the current object.");
                        }
                    }
                }

                vCompiledCollection.BindingField = vCollection.BindingField;
                vCompiledCollection.Description = vCollection.Description;
                vCompiledCollection.Filename = vCollection.Object;
                vCompiledCollection.Object = vCollection.Object;

                if (String.IsNullOrEmpty(vCollection.Alias))
                {
                    vCompiledCollection.Alias = vCollection.Name.ToLower();
                }
                else
                {
                    vCompiledCollection.Alias = vCollection.Alias;
                }

                pTarget.Collections.Add(vCompiledCollection);
            }

            // Compile collections implied from intersections.
            SourceModel.IntersectionCollection vIntersections = mSourceProject.Intersections.GetCollectionsByObject(pSource);
            SourceModel.ReferenceDetail vReference = null;

            foreach (SourceModel.IntersectionDetail vIntersection in vIntersections)
            {
                vReference = vIntersection.References.GetReferenceFromObject(pSource);

                vCompiledCollection = new CompiledModel.CollectionDetail();

                vCompiledCollection.Name = vReference.Accessor;
                vCompiledCollection.Object = vReference.Object;
                vCompiledCollection.BindingObject = vIntersection.Name;
                vCompiledCollection.BindingField = vReference.Name;
                vCompiledCollection.Alias = vReference.Alias;
                vCompiledCollection.Filename = vReference.Object;

                pTarget.Collections.Add(vCompiledCollection);
            }
        }

        protected virtual void OnCompileObject(SourceModel.ObjectDetail pSource, CompiledModel.TableDetail pTarget)
        {
            CompiledModel.FieldDetail vCompiledField = null;
            CompiledModel.ImplementDetail vCompiledImplement = null;
            CompiledModel.PermissionDetail vCompiledPermission = null;
            CompiledModel.CapabilityDetail vCompiledCapability = null;

            pTarget.Name = pSource.Name;
            pTarget.Description = pSource.Description;
            pTarget.Alias = pSource.Alias;
            pTarget.Filename = pSource.Name;
            pTarget.Namespace = mCompiledProject.DefaultNamespace;
            pTarget.FriendlyName = pSource.FriendlyName;
            pTarget.LogicalName = pSource.LogicalName;
            pTarget.Filegroup = pSource.Filegroup;

            foreach (SourceModel.ImplementDetail vImplement in pSource.Implements)
            {
                vCompiledImplement = new CompiledModel.ImplementDetail();

                vCompiledImplement.Ref = vImplement.Ref;

                pTarget.Implements.Add(vCompiledImplement);
            }

            foreach (SourceModel.PermissionDetail vPermission in pSource.Permissions)
            {
                vCompiledPermission = new CompiledModel.PermissionDetail();

                vCompiledPermission.Role = vPermission.Role;

                foreach (SourceModel.CapabilityDetail vCapability in vPermission.Capabilities)
                {
                    vCompiledCapability = new CompiledModel.CapabilityDetail();

                    vCompiledCapability.Name = vCapability.Name;

                    vCompiledPermission.Capabilities.Add(vCompiledCapability);
                }

                pTarget.Permissions.Add(vCompiledPermission);
            }

            // Fields are added to the Table specified in DBTable.
            CompiledModel.TableDetail vPersistTable = null;

            if (pSource.DBTable != pSource.Name)
            {
                vPersistTable = mCompiledProject.Tables[pSource.DBTable];
            }
            else
            {
                vPersistTable = pTarget;
            }

            foreach (SourceModel.FieldDetail vField in pSource.Fields)
            {
                vCompiledField = new CompiledModel.FieldDetail();

                this.OnCompileField(vField, vCompiledField);

                vPersistTable.Fields.Add(vCompiledField);
            }
        }
        #endregion

        #region Field Compilation
        protected virtual void OnCompileField(SourceModel.FieldDetail pSource, CompiledModel.FieldDetail pTarget)
        {
            CompiledModel.IndexDetail vCompiledIndex = null;
            CompiledModel.ConstraintDetail vCompiledConstraint = null;
            CompiledModel.ConstraintValueDetail vCompiledConstraintValue = null;

            SourceModel.FieldDetail vSourcePKField = null;
            SourceModel.ObjectDetail vSourceFKObject = null;

            pTarget.Name = pSource.Name;
            pTarget.Alias = pSource.Alias;
            pTarget.AutoGenerate = (bool)pSource.AutoGenerate;
            pTarget.DataType = pSource.DataType;
            pTarget.DefaultValue = pSource.DefaultValue;
            pTarget.Description = pSource.Description;
            pTarget.FriendlyName = pSource.FriendlyName;
            pTarget.LogicalName = pSource.LogicalName;
            pTarget.ReadOnly = pSource.ReadOnly;
            pTarget.Required = pSource.Required;
            pTarget.Scale = pSource.Scale;
            pTarget.Size = pSource.Size;
            pTarget.TableOnly = pSource.TableOnly;
            pTarget.DBName = pSource.Name;

            if (pSource.IsForeignKey && !pSource.IsPrimaryKey)
            {
                pTarget.DBName += CompilerSettings.DBNAME_FKSUFFIX;

                vSourceFKObject = mSourceProject.Objects[pSource.GetForeignKey().Object];
                vSourcePKField = vSourceFKObject.GetPKField();

                pTarget.DataType = this.GetDBType(vSourcePKField.DataType);
                pTarget.DBType = pTarget.DataType;
            }
            else
            {
                pTarget.DBType = this.GetDBType(pSource.DataType);
            }

            foreach (SourceModel.IndexDetail vIndex in pSource.Indexes)
            {
                vCompiledIndex = new CompiledModel.IndexDetail();

                vCompiledIndex.Alias = vIndex.Alias;
                vCompiledIndex.Accessor = vIndex.Accessor;
                vCompiledIndex.Description = vIndex.Description;
                vCompiledIndex.Name = vIndex.Name;
                vCompiledIndex.ConstraintName = vIndex.ConstraintName;
                vCompiledIndex.Object = vIndex.Object;
                vCompiledIndex.OwnerField = vIndex.OwnerField;
                vCompiledIndex.Type = vIndex.Type;
                vCompiledIndex.Filegroup = vIndex.Filegroup;
                vCompiledIndex.CascadeDelete = vIndex.CascadeDelete;

                pTarget.Indexes.Add(vCompiledIndex);
            }

            foreach (SourceModel.ConstraintDetail vConstraint in pSource.Constraints)
            {
                vCompiledConstraint = new CompiledModel.ConstraintDetail();

                vCompiledConstraint.Name = vConstraint.Name;
                vCompiledConstraint.Description = vConstraint.Description;
                vCompiledConstraint.Operation = vConstraint.Operation;
                vCompiledConstraint.Modifier = vConstraint.Modifier;
                vCompiledConstraint.Value = vConstraint.Value;
                vCompiledConstraint.MinValue = vConstraint.MinValue;
                vCompiledConstraint.MaxValue = vConstraint.MaxValue;

                foreach (SourceModel.ConstraintValueDetail vConstraintValue in vConstraint.Values)
                {
                    vCompiledConstraintValue = new CompiledModel.ConstraintValueDetail();

                    vCompiledConstraintValue.Value = vConstraintValue.Value;

                    vCompiledConstraint.Values.Add(vCompiledConstraintValue);
                }

                pTarget.Constraints.Add(vCompiledConstraint);
            }
        }
        #endregion

        #region Method Compilation
        protected virtual void OnCompileMethods()
        {
            CompiledModel.ObjectDetail vCompiledObject = null;
            CompiledModel.PackageDetail vCompiledPackage = null;

            foreach (SourceModel.ObjectDetail vObject in mSourceProject.Objects)
            {
                // Find the associated Compiled Object.
                vCompiledObject = mCompiledProject.Objects[vObject.Name];

                vCompiledObject.Project = mCompiledProject;

                foreach (SourceModel.MethodDetail vMethod in vObject.Methods)
                {
                    vMethod.Initialize();

                    this.OnCompileMethod(vMethod, vCompiledObject);
                }
            }

            foreach (SourceModel.PackageDetail vPackage in mSourceProject.Packages)
            {
                // Find the associated Compiled Package.
                vCompiledPackage = mCompiledProject.Packages[vPackage.Name];

                //				vCompiledPackage.Project = mCompiledProject;

                CompiledModel.MethodDetail vCompiledMethod = null;

                foreach (SourceModel.MethodDetail vMethod in vPackage.Methods)
                {
                    vMethod.Initialize();

                    vCompiledMethod = new CompiledModel.MethodDetail();

                    vCompiledMethod.Name = vMethod.Name;
                    vCompiledMethod.ObjectName = vMethod.ObjectName;
                    vCompiledMethod.DBName = vMethod.DBName;
                    vCompiledMethod.Description = vMethod.Description;
                    vCompiledMethod.MethodType = vMethod.MethodType;
                    vCompiledMethod.ReturnResultSet = vMethod.ReturnResultSet;

                    this.OnCompileCustomMethod(vMethod, vCompiledMethod);

                    vCompiledPackage.Methods.Add(vCompiledMethod);
                }
            }
        }

        protected virtual void OnCompileMethod(SourceModel.MethodDetail pSource, CompiledModel.ObjectDetail pTargetObject)
        {
            CompiledModel.MethodDetail vCompiledMethod = new CompiledModel.MethodDetail();
            CompiledModel.TableDetail vCompiledTable = mCompiledProject.Tables[pTargetObject.Name];

            vCompiledMethod.Name = pSource.Name;
            vCompiledMethod.ObjectName = pSource.ObjectName;
            vCompiledMethod.DBName = pSource.DBName;
            vCompiledMethod.Description = pSource.Description;
            vCompiledMethod.MethodType = pSource.MethodType;

            CompiledModel.TableDetail vTargetFields = mCompiledProject.Tables[pSource.ParentObject.DBTable];

            this.OnCompileMethodSecurity(pSource, vCompiledMethod, vCompiledTable);

            switch (pSource.MethodType)
            {
                case CompilerSettings.METHODTYPE_GET:
                    {
                        this.OnCompileGetMethod(pSource, vCompiledMethod, pTargetObject);
                        vCompiledMethod.ReturnResultSet = true;

                        break;
                    }
                case CompilerSettings.METHODTYPE_LIST:
                    {
                        this.OnCompileListMethod(pSource, vCompiledMethod, pTargetObject);
                        vCompiledMethod.ReturnResultSet = true;

                        break;
                    }
                case CompilerSettings.METHODTYPE_CREATE:
                    {
                        this.OnCompileCreateMethod(pSource, vCompiledMethod, pTargetObject);
                        vCompiledMethod.ReturnResultSet = false;

                        break;
                    }
                case CompilerSettings.METHODTYPE_UPDATE:
                    {
                        this.OnCompileUpdateMethod(pSource, vCompiledMethod, pTargetObject);
                        vCompiledMethod.ReturnResultSet = false;

                        break;
                    }
                case CompilerSettings.METHODTYPE_CUSTOM:
                    {
                        this.OnCompileCustomMethod(pSource, vCompiledMethod);
                        vCompiledMethod.ReturnResultSet = pSource.ReturnResultSet;

                        // If the method says not to return a result set, but there are results specified, thow an exception.
                        if (!pSource.ReturnResultSet && (vCompiledMethod.ResultObjects.Count > 0))
                        {
                            throw new ApplicationException("SourceCompiler.OnCompileMethod( SourceModel.MethodDetail, CompiledModel.ObjectDetail ) failed: Method [ " + pTargetObject.Name + "." + pSource.Name + " ] specifies no results, but there are result objects listed.");
                        }

                        break;
                    }
                default:
                    {
                        throw new ApplicationException("SourceCompiler.OnCompileMethod( SourceModel.MethodDetail, CompiledModel.ObjectDetail ) failed: The Method type [ " + pSource.MethodType + " ] is invalid.");
                    }
            }

            vCompiledTable.Methods.Add(vCompiledMethod);
        }

        protected virtual void OnCompileGetMethod(SourceModel.MethodDetail pSource, CompiledModel.MethodDetail pTarget, CompiledModel.ObjectDetail pTargetObject)
        {
            CompiledModel.FieldDetail vPKField = pTargetObject.GetPKField();

            CompiledModel.ParameterDetail vCompiledParameter = null;
            CompiledModel.ResultObjectRefDetail vCompiledResultObject = null;
            CompiledModel.ResultDetail vCompiledResult = null;
            CompiledModel.ObjectDetail vCompiledObject = null;
            CompiledModel.FilterDetail vCompiledFilter = null;

            if (String.IsNullOrEmpty(pTarget.Name))
            {
                pTarget.Name = pTargetObject.Name + pSource.MethodType;
            }

            pTarget.Filename = pTarget.DBName;

            pTarget.Description = pSource.Description;

            // Use the PK of the table as the parameter.
            vCompiledParameter = vPKField.ConvertToParameter();
            vCompiledParameter.Description = vPKField.Description;
            vCompiledParameter.Direction = CompilerSettings.PARAMDIR_IN;

            pTarget.Parameters.Add(vCompiledParameter);

            // Results should be all fields that are not the Primary Key.
            foreach (CompiledModel.ObjectRefDetail vObjectRef in pTargetObject.AllFields)
            {
                vCompiledObject = mCompiledProject.Objects[vObjectRef.Ref];
                vCompiledResultObject = pTarget.ResultObjects[vCompiledObject.DBTable];

                if (vCompiledResultObject == null)
                {
                    vCompiledResultObject = new CompiledModel.ResultObjectRefDetail();
                }

                if (vCompiledObject != null)
                {
                    vCompiledResultObject.Name = vCompiledObject.DBTable;
                    vCompiledResultObject.Ref = vCompiledObject.DBTable;
                    vCompiledResultObject.Alias = vCompiledObject.Alias;
                    vCompiledResultObject.PKField = vCompiledObject.GetPKField().Name;
                    vCompiledResultObject.FKField = vCompiledResultObject.PKField;
                }

                foreach (CompiledModel.FieldDetail vField in vObjectRef.Fields)
                {
                    vCompiledResult = new CompiledModel.ResultDetail();

                    vCompiledResult.Field = vField.Name;
                    vCompiledResult.Alias = vField.Alias;
                    vCompiledResult.Type = vField.DBType;
                    vCompiledResult.Size = vField.Size;
                    vCompiledResult.Scale = vField.Scale;
                    vCompiledResult.DBName = vField.DBName;
                    vCompiledResult.IsForeignKey = vField.IsForeignKey;

                    vCompiledResultObject.Fields.Add(vCompiledResult);
                }

                if (pTarget.ResultObjects.IndexOf(vCompiledResultObject) == -1)
                {
                    pTarget.ResultObjects.Add(vCompiledResultObject);
                }
            }

            // The Filter should be based on the PK param.
            vCompiledFilter = new CompiledModel.FilterDetail();
            vCompiledFilter.Field = vPKField.Name;
            vCompiledFilter.Param = DataUtility.CamelCase(vCompiledParameter.Name);
            vCompiledFilter.DBName = vPKField.DBName;

            pTarget.Filters.Add(vCompiledFilter);
        }

        protected virtual void OnCompileListMethod(SourceModel.MethodDetail pSource, CompiledModel.MethodDetail pTarget, CompiledModel.ObjectDetail pTargetObject)
        {
            CompiledModel.FilterDetail vCompiledFilter = null;
            CompiledModel.FieldDetail vCompiledFilterField = null;
            CompiledModel.ParameterDetail vCompiledParam = null;
            CompiledModel.ResultObjectRefDetail vCompiledResultObject = null;
            CompiledModel.ResultDetail vCompiledResult = null;
            CompiledModel.ObjectDetail vCompiledObject = null;
            CompiledModel.ObjectDetail vCompiledFKObject = null;
            CompiledModel.ResultObjectRefDetail vCompiledObjectRef = null;
            CompiledModel.FieldDetail vCompiledFKTitle = null;
            CompiledModel.ResultDetail vCompiledFKTitleLookup = null;

            int vContextTableCounter = 0;

            if (String.IsNullOrEmpty(pTarget.Name))
            {
                pTarget.Name = pTargetObject.Name + pSource.MethodType;
            }

            pTarget.Filename = pTarget.DBName;

            pTarget.Description = pSource.Description;

            // For each filter value in the definition.
            foreach (SourceModel.FilterDetail vFilter in pSource.Filters)
            {
                vCompiledFilter = new CompiledModel.FilterDetail();

                vCompiledFilter.Field = vFilter.Field;
                vCompiledFilter.Param = DataUtility.CamelCase(vFilter.Param);
                vCompiledFilter.Value = vFilter.Value;

                pTarget.Filters.Add(vCompiledFilter);

                if (!String.IsNullOrEmpty(vFilter.Param))
                {
                    // Find the Field in the parent object.
                    vCompiledFilterField = pTargetObject.Fields[vFilter.Param];

                    if (vCompiledFilterField == null)
                    {
                        throw new Exception(String.Format("The Param [{0}] specified by filter on method [{1}] of object [{2}] does not exist.", vFilter.Param, pTarget.Name, pTargetObject.Name));
                    }

                    vCompiledFilter.DBName = vCompiledFilterField.DBName;

                    // Only generate a Parameter if there is no Value specified.
                    if (String.IsNullOrEmpty(vFilter.Value) && (pTarget.Parameters[vFilter.Param] == null))
                    {
                        vCompiledParam = vCompiledFilterField.ConvertToParameter();
                        vCompiledParam.Required = true;

                        pTarget.Parameters.Add(vCompiledParam);
                    }
                }
                else
                {
                    throw new Exception("Compliation of Method [ {0}.{1} ] failed: The Filter [ {2} ] does not specify a Param name.");
                }
            }

            // Results should be all fields.
            foreach (CompiledModel.ObjectRefDetail vObjectRef in pTargetObject.AllFields)
            {
                vCompiledObject = mCompiledProject.Objects[vObjectRef.Ref];
                vCompiledResultObject = pTarget.ResultObjects[vCompiledObject.DBTable];

                if (vCompiledResultObject == null)
                {
                    vCompiledResultObject = new CompiledModel.ResultObjectRefDetail();
                }

                if (vCompiledObject != null)
                {
                    vCompiledResultObject.Name = vCompiledObject.DBTable;
                    vCompiledResultObject.Ref = vCompiledObject.DBTable;
                    vCompiledResultObject.Alias = vCompiledObject.Alias;
                    vCompiledResultObject.PKField = vCompiledResultObject.PKField;
                    vCompiledResultObject.FKField = vCompiledObject.GetPKField().Name;
                    vCompiledResultObject.Required = true;
                }

                foreach (CompiledModel.FieldDetail vField in vObjectRef.Fields)
                {
                    vCompiledResult = new CompiledModel.ResultDetail();

                    vCompiledResult.Field = vField.Name;
                    vCompiledResult.Type = vField.DBType;
                    vCompiledResult.Size = vField.Size;
                    vCompiledResult.Scale = vField.Scale;
                    vCompiledResult.DBName = vField.DBName;
                    vCompiledResult.Alias = vField.DBName;
                    vCompiledResult.IsForeignKey = vField.IsForeignKey;

                    vCompiledResultObject.Fields.Add(vCompiledResult);

                    if (vField.IsForeignKey && !vField.IsPrimaryKey)
                    {
                        CompiledModel.IndexDetail vCompiledFKIndex = vField.GetForeignKey();

                        vCompiledFKObject = mCompiledProject.Objects[vCompiledFKIndex.Object];

                        if (!DataUtility.IsEmpty(vCompiledFKObject.TitleField))
                        {
                            vCompiledObjectRef = new CompiledModel.ResultObjectRefDetail();
                            vCompiledFKTitle = vCompiledFKObject.Fields[vCompiledFKObject.TitleField];
                            vCompiledFKTitleLookup = new CompiledModel.ResultDetail();

                            vCompiledObjectRef.Name = vCompiledFKIndex.Accessor;
                            vCompiledObjectRef.Alias = vCompiledFKIndex.Alias;
                            vCompiledObjectRef.Ref = vCompiledFKObject.DBTable;
                            vCompiledObjectRef.PKField = vCompiledFKObject.GetPKField().Name;
                            vCompiledObjectRef.FKField = vField.DBName;
                            vCompiledObjectRef.Required = (bool)vField.Required;

                            vCompiledFKTitleLookup.Name = vCompiledFKTitle.Name;
                            vCompiledFKTitleLookup.Field = vCompiledFKTitle.Name;
                            vCompiledFKTitleLookup.DBName = vCompiledFKTitle.Name;
                            vCompiledFKTitleLookup.Alias = vCompiledObjectRef.Name + vCompiledFKTitle.Name;
                            vCompiledFKTitleLookup.Type = vCompiledFKTitle.DataType;
                            vCompiledFKTitleLookup.Size = vCompiledFKTitle.Size;
                            vCompiledFKTitleLookup.Scale = vCompiledFKTitle.Scale;
                            vCompiledFKTitleLookup.IsForeignKey = vCompiledFKTitle.IsForeignKey;

                            vCompiledObjectRef.Fields.Add(vCompiledFKTitleLookup);

                            pTarget.ResultObjects.Add(vCompiledObjectRef);

                        }
                    }
                }

                if (pTarget.ResultObjects.IndexOf(vCompiledResultObject) == -1)
                {
                    pTarget.ResultObjects.AddAt(vContextTableCounter, vCompiledResultObject);

                    vContextTableCounter++;
                }
            }
        }

        protected virtual void OnCompileCreateMethod(SourceModel.MethodDetail pSource, CompiledModel.MethodDetail pTarget, CompiledModel.ObjectDetail pTargetObject)
        {
            CompiledModel.ParameterDetail vCompiledParameter = null;
            CompiledModel.EffectObjectRefDetail vCompiledEffectObject = null;
            CompiledModel.ObjectDetail vCompiledObject = null;
            CompiledModel.EffectDetail vCompiledEffect = null;

            if (String.IsNullOrEmpty(pTarget.Name))
            {
                pTarget.Name = pTargetObject.Name + pSource.MethodType;
            }

            pTarget.Filename = pTarget.DBName;

            pTarget.Description = pSource.Description;

            // Add a Parameter for each field in the list.
            foreach (CompiledModel.ObjectRefDetail vObjectRef in pTargetObject.AllFields)
            {
                foreach (CompiledModel.FieldDetail vField in vObjectRef.Fields)
                {
                    if (pTarget.Parameters[vField.Name] == null)
                    {
                        vCompiledParameter = vField.ConvertToParameter();

                        vCompiledParameter.Direction = ((bool)vField.AutoGenerate) ? CompilerSettings.PARAMDIR_OUT : CompilerSettings.PARAMDIR_IN;

                        pTarget.Parameters.Add(vCompiledParameter);
                    }
                }
            }

            // All fields except for IDENTITY fields should be affected by the operation.
            foreach (CompiledModel.ObjectRefDetail vObjectRef in pTargetObject.AllFields)
            {
                vCompiledObject = mCompiledProject.Objects[vObjectRef.Ref];
                vCompiledEffectObject = pTarget.AffectedObjects[vCompiledObject.DBTable];

                if (vCompiledEffectObject == null)
                {
                    vCompiledEffectObject = new CompiledModel.EffectObjectRefDetail();
                    vCompiledEffectObject.EffectType = pTarget.MethodType;
                }

                if (vCompiledObject != null)
                {
                    vCompiledEffectObject.Name = vCompiledObject.DBTable;
                    vCompiledEffectObject.Ref = vCompiledObject.DBTable;
                    vCompiledEffectObject.Alias = vCompiledObject.Alias;
                    vCompiledEffectObject.PKField = vCompiledObject.GetPKField().Name;
                    vCompiledEffectObject.FKField = vCompiledEffectObject.PKField;
                }

                foreach (CompiledModel.FieldDetail vField in vCompiledObject.Fields)
                {
                    if (vField.DataType != CompilerSettings.DATATYPE_IDENTITY)
                    {
                        vCompiledEffect = new CompiledModel.EffectDetail();

                        vCompiledEffect.Field = vField.Name;
                        vCompiledEffect.Param = DataUtility.CamelCase(vField.Name);
                        vCompiledEffect.Type = vField.DataType;
                        vCompiledEffect.DBType = vField.DBType;
                        vCompiledEffect.DBName = vField.DBName;
                        vCompiledEffect.AutoGenerate = vField.AutoGenerate;

                        vCompiledEffectObject.Fields.Add(vCompiledEffect);
                    }
                }

                if (pTarget.AffectedObjects.IndexOf(vCompiledEffectObject) == -1)
                {
                    pTarget.AffectedObjects.Add(vCompiledEffectObject);
                }
            }
        }

        protected virtual void OnCompileUpdateMethod(SourceModel.MethodDetail pSource, CompiledModel.MethodDetail pTarget, CompiledModel.ObjectDetail pTargetObject)
        {
            CompiledModel.FieldDetail vPKField = pTargetObject.GetPKField();

            CompiledModel.ParameterDetail vCompiledParameter = null;
            CompiledModel.EffectObjectRefDetail vCompiledEffectObject = null;
            CompiledModel.ObjectDetail vCompiledObject = null;
            CompiledModel.EffectDetail vCompiledEffect = null;
            CompiledModel.FilterDetail vCompiledFilter = null;
            CompiledModel.FieldDetail vCompiledField = null;

            if (String.IsNullOrEmpty(pTarget.Name))
            {
                pTarget.Name = pTargetObject.Name + pSource.MethodType;
            }

            if (DataUtility.IsEmpty(pTarget.DBName))
            {
                pTarget.DBName = pTargetObject.Name + pSource.MethodType;
            }

            pTarget.Filename = pTarget.DBName;

            pTarget.Description = pSource.Description;

            if (pSource.IsCustom)
            {
                // If we have a custom implementation, parameters should be the filters and affected fields.
                vCompiledEffectObject = new CompiledModel.EffectObjectRefDetail();

                vCompiledEffectObject.EffectType = pTarget.MethodType;
                vCompiledEffectObject.Name = pTargetObject.DBTable;
                vCompiledEffectObject.Ref = pTargetObject.DBTable;
                vCompiledEffectObject.Alias = pTargetObject.Alias;
                vCompiledEffectObject.PKField = pTargetObject.GetPKField().Name;

                pTarget.AffectedObjects.Add(vCompiledEffectObject);

                foreach (SourceModel.FilterDetail vFilter in pSource.Filters)
                {
                    vCompiledFilter = new CompiledModel.FilterDetail();
                    vCompiledFilter.Field = vFilter.Field;
                    vCompiledFilter.Param = DataUtility.CamelCase(vFilter.Field);

                    vCompiledEffectObject.Filters.Add(vCompiledFilter);

                    vCompiledField = pTargetObject.Fields[vFilter.Field];

                    vCompiledFilter.DBName = vCompiledField.DBName;

                    vCompiledParameter = vCompiledField.ConvertToParameter();
                    pTarget.Parameters.Add(vCompiledParameter);
                }

                foreach (SourceModel.FieldDetail vField in pSource.AffectedFields)
                {
                    vCompiledField = pTargetObject.Fields[vField.Name];

                    if (vCompiledField == null)
                    {
                        throw new ApplicationException(String.Format("SourceCompiler.OnCompileUpdateMethod for {0}.{1} failed: The affected field {2} cannot be found.", pTargetObject.Name, pTarget.Name, vField.Name));
                    }

                    vCompiledEffect = new CompiledModel.EffectDetail();

                    vCompiledEffect.Field = vCompiledField.Name;
                    vCompiledEffect.Param = DataUtility.CamelCase(vCompiledField.Name);
                    vCompiledEffect.Type = vCompiledField.DataType;
                    vCompiledEffect.DBType = vCompiledField.DBType;
                    vCompiledEffect.AutoGenerate = vCompiledField.AutoGenerate;
                    vCompiledEffect.DBName = vCompiledField.DBName;

                    vCompiledParameter = vCompiledField.ConvertToParameter();
                    pTarget.Parameters.Add(vCompiledParameter);

                    vCompiledEffectObject.Fields.Add(vCompiledEffect);
                }
            }
            else
            {
                // Results should be all fields that are not the Primary Key.
                foreach (CompiledModel.ObjectRefDetail vObjectRef in pTargetObject.AllFields)
                {
                    foreach (CompiledModel.FieldDetail vField in vObjectRef.Fields)
                    {
                        if (pTarget.Parameters[vField.Name] == null)
                        {
                            if ((!(bool)vField.AutoGenerate) || (vField.GetIsPrimaryKey()))
                            {
                                vCompiledParameter = vField.ConvertToParameter();

                                pTarget.Parameters.Add(vCompiledParameter);
                            }
                        }
                    }
                }

                foreach (CompiledModel.ObjectRefDetail vObjectRef in pTargetObject.AllFields)
                {
                    vCompiledObject = mCompiledProject.Objects[vObjectRef.Ref];
                    vCompiledEffectObject = pTarget.AffectedObjects[vCompiledObject.DBTable];

                    if (vCompiledEffectObject == null)
                    {
                        vCompiledEffectObject = new CompiledModel.EffectObjectRefDetail();
                        vCompiledEffectObject.EffectType = pTarget.MethodType;
                    }

                    if (vCompiledObject != null)
                    {
                        vCompiledEffectObject.Name = vCompiledObject.DBTable;
                        vCompiledEffectObject.Ref = vCompiledObject.DBTable;
                        vCompiledEffectObject.Alias = vCompiledObject.Alias;
                        vCompiledEffectObject.PKField = vCompiledObject.GetPKField().Name;
                        vCompiledEffectObject.FKField = vCompiledEffectObject.PKField;
                    }

                    foreach (CompiledModel.FieldDetail vField in vCompiledObject.Fields)
                    {
                        if ((vField.Name != vPKField.Name) && !vField.ReadOnly)
                        {
                            vCompiledEffect = new CompiledModel.EffectDetail();

                            vCompiledEffect.Field = vField.Name;
                            vCompiledEffect.Param = DataUtility.CamelCase(vField.Name);
                            vCompiledEffect.Type = vField.DataType;
                            vCompiledEffect.DBType = vField.DBType;
                            vCompiledEffect.AutoGenerate = vField.AutoGenerate;
                            vCompiledEffect.DBName = vField.DBName;

                            vCompiledEffectObject.Fields.Add(vCompiledEffect);
                        }
                    }

                    if (pTarget.AffectedObjects.IndexOf(vCompiledEffectObject) == -1)
                    {
                        pTarget.AffectedObjects.Add(vCompiledEffectObject);
                    }
                }

                // The Filter should be based on the PK param.
                vCompiledFilter = new CompiledModel.FilterDetail();
                vCompiledFilter.Field = vPKField.Name;
                vCompiledFilter.Param = DataUtility.CamelCase(vPKField.Name);
                vCompiledFilter.DBName = vPKField.DBName;

                vCompiledEffectObject.Filters.Add(vCompiledFilter);

            }
        }

        protected virtual void OnCompileCustomMethod(SourceModel.MethodDetail pSource, CompiledModel.MethodDetail pTarget)
        {
            CompiledModel.ParameterDetail vCompiledParameter = null;
            CompiledModel.EffectObjectRefDetail vCompiledObjectRef = null;
            CompiledModel.EffectDetail vCompiledEffect = null;

            CompiledModel.ObjectDetail vCompiledObject = null;
            CompiledModel.FieldDetail vCompiledField = null;

            pTarget.Name = pSource.Name;
            pTarget.Filename = pSource.DBName;

            // For Custom procedures, copy over the Parameter declarations.
            foreach (SourceModel.ParameterDetail vSourceParameter in pSource.Parameters)
            {
                vCompiledParameter = new CompiledModel.ParameterDetail();

                vCompiledParameter.Name = DataUtility.CamelCase(vSourceParameter.Name);
                vCompiledParameter.DBName = DataUtility.CamelCase(vSourceParameter.Name);
                vCompiledParameter.Type = vSourceParameter.Type;
                vCompiledParameter.Size = vSourceParameter.Size;
                vCompiledParameter.Scale = vSourceParameter.Scale;
                vCompiledParameter.Direction = vSourceParameter.Direction;
                vCompiledParameter.Description = vSourceParameter.Description;

                if (String.IsNullOrEmpty(vSourceParameter.Direction))
                {
                    vCompiledParameter.Direction = "in";
                }
                else
                {
                    vCompiledParameter.Direction = vSourceParameter.Direction;
                }

                vCompiledParameter.Required = vSourceParameter.Required;

                pTarget.Parameters.Add(vCompiledParameter);
            }

            foreach (SourceModel.EffectObjectRefDetail vSourceObjectRef in pSource.AffectedObjects)
            {
                vCompiledObjectRef = new CompiledModel.EffectObjectRefDetail();

                vCompiledObjectRef.Name = vSourceObjectRef.Name;
                vCompiledObjectRef.Description = vSourceObjectRef.Description;

                if (String.IsNullOrEmpty(vSourceObjectRef.EffectType))
                {
                    vCompiledObjectRef.EffectType = pTarget.MethodType;
                }
                else
                {
                    vCompiledObjectRef.EffectType = vSourceObjectRef.EffectType;
                }

                vCompiledObjectRef.Ref = vSourceObjectRef.Ref;

                vCompiledObject = mCompiledProject.Objects[vSourceObjectRef.Ref];

                if (vCompiledObject == null)
                {
                    throw new ApplicationException(String.Format("OnCompileCustomMethod failed for method [ {1} ].  The object [ {2} ] referenced in the Effects does not exist.", pTarget.Name, vSourceObjectRef.Ref));
                }

                vCompiledObjectRef.Alias = vCompiledObject.Alias;

                foreach (SourceModel.EffectDetail vSourceEffect in vSourceObjectRef.Fields)
                {
                    vSourceEffect.Initialize();

                    vCompiledEffect = new CompiledModel.EffectDetail();

                    vCompiledEffect.Name = vSourceEffect.Name;
                    vCompiledEffect.Description = vSourceEffect.Description;
                    vCompiledEffect.Field = vSourceEffect.Field;

                    vCompiledField = vCompiledObject.Fields[vSourceEffect.Field];

                    if (vCompiledField == null)
                    {
                        throw new ApplicationException(String.Format("OnCompileCustomMethod failed for method [ {1} ].  The field [ {2}.{3} ] referenced in the Effects does not exist.", pTarget.Name, vSourceObjectRef.Ref, vSourceEffect.Field));
                    }

                    vCompiledEffect.DBName = vCompiledField.DBName;
                    vCompiledEffect.DBType = vCompiledField.DBType;
                    vCompiledEffect.Param = DataUtility.CamelCase(vSourceEffect.Param);

                    vCompiledObjectRef.Fields.Add(vCompiledEffect);
                }

                pTarget.AffectedObjects.Add(vCompiledObjectRef);
            }
        }
        #endregion

        #region Package Compilation
        protected virtual void OnCompilePackages(SourceModel.PackageCollection pSource)
        {
            foreach (SourceModel.PackageDetail vPackage in pSource)
            {
                this.OnCompilePackage(vPackage);
            }
        }

        protected virtual void OnCompilePackage(SourceModel.PackageDetail pSource)
        {
            CompiledModel.PackageDetail vCompiledPackage = new CompiledModel.PackageDetail();

            if (DataUtility.IsEmpty(pSource.Name))
            {
                throw new ApplicationException("SourceCompiler.OnCompilePackage( SourceModel.PackageDetail ) failed: The Package Name property is required.");
            }

            this.OnCompilePackage(pSource, vCompiledPackage);

            mCompiledProject.Packages.Add(vCompiledPackage);
        }

        protected virtual void OnCompilePackage(SourceModel.PackageDetail pSource, CompiledModel.PackageDetail pTarget)
        {
            pTarget.Name = pSource.Name;
            pTarget.Description = pSource.Description;
        }

        #endregion

        #region Compile Security
        protected virtual void OnCompileSecurity(SourceModel.SecurityDetail pSource)
        {
            CompiledModel.RoleDetail vCompiledRole = null;
            CompiledModel.UserDetail vCompiledUser = null;

            foreach (SourceModel.RoleDetail vSourceRole in pSource.Roles)
            {
                vCompiledRole = new CompiledModel.RoleDetail();
                vCompiledRole.Name = vSourceRole.Name;
                vCompiledRole.Description = vSourceRole.Description;

                mCompiledProject.Security.Roles.Add(vCompiledRole);
            }

            foreach (SourceModel.UserDetail vSourceUser in pSource.Users)
            {
                vCompiledUser = new CompiledModel.UserDetail();
                vCompiledUser.Type = vSourceUser.Type;
                vCompiledUser.Name = vSourceUser.Name;
                vCompiledUser.Password = vSourceUser.Password;
                vCompiledUser.Description = vSourceUser.Description;

                foreach (SourceModel.RoleDetail vSourceUserRole in vSourceUser.Roles)
                {
                    vCompiledRole = new CompiledModel.RoleDetail();

                    vCompiledRole.Name = vSourceUserRole.Name;

                    vCompiledUser.Roles.Add(vCompiledRole);
                }

                mCompiledProject.Security.Users.Add(vCompiledUser);
            }
        }

        protected virtual void OnCompileMethodSecurity(SourceModel.MethodDetail pSource, CompiledModel.MethodDetail pTarget, CompiledModel.TableDetail pTargetTable)
        {
            CompiledModel.PermissionDetail vCompiledPermission = null;
            CompiledModel.CapabilityDetail vCompiledCapability = null;

            foreach (SourceModel.PermissionDetail vPermission in pSource.Permissions)
            {
                vCompiledPermission = new CompiledModel.PermissionDetail();

                vCompiledPermission.Role = vPermission.Role;

                foreach (SourceModel.CapabilityDetail vCapability in vPermission.Capabilities)
                {
                    vCompiledCapability = new CompiledModel.CapabilityDetail();

                    vCompiledCapability.Name = vCapability.Name;

                    vCompiledPermission.Capabilities.Add(vCompiledCapability);
                }

                pTarget.Permissions.Add(vCompiledPermission);
            }

        }
        #endregion

        #endregion
    }
}
